<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#mixin"  class="anchor" id="user-content-mixin">

    </a>Mixin
    </h1>
    <p>JavaScript 语言的设计是单一继承，即子类只能继承一个父类，不允许继承多个父类。这种设计保证了对象继承的层次结构是树状的，而不是复杂的<a
            href="https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem" rel="nofollow">网状结构</a>。</p>
    <p>但是，这大大降低了编程的灵活性。因为实际开发中，有时不可避免，子类需要继承多个父类。举例来说，“猫”可以继承“哺乳类动物”，也可以继承“宠物”。</p>
    <p>各种单一继承的编程语言，有不同的多重继承解决方案。比如，Java 语言也是子类只能继承一个父类，但是还允许继承多个界面（interface），这样就间接实现了多重继承。Interface
        与父类一样，也是一个类，只不过它只定义接口（method signature），不定义实现，因此又被称为“抽象类”。凡是继承于 Interface
        的方法，都必须自己定义实现，否则就会报错。这样就避免了多重继承的最大问题：多个父类的同名方法的碰撞（naming collision）。</p>
    <p>JavaScript 语言没有采用 Interface 的方案，而是通过代理（delegation）实现了从其他类引入方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> <span
            class="pl-en">Enumerable_first</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () {
  <span class="pl-c1">this</span>.<span class="pl-en">first</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>[<span class="pl-c1">0</span>];
  };
};

<span class="pl-k">var</span> list <span class="pl-k">=</span> [<span class="pl-s"><span class="pl-pds">"</span>foo<span
                class="pl-pds">"</span></span>, <span class="pl-s"><span class="pl-pds">"</span>bar<span class="pl-pds">"</span></span>, <span
                class="pl-s"><span class="pl-pds">"</span>baz<span class="pl-pds">"</span></span>];
<span class="pl-smi">Enumerable_first</span>.<span class="pl-c1">call</span>(list); <span class="pl-c"><span
                class="pl-c">//</span> explicit delegation</span>
<span class="pl-smi">list</span>.<span class="pl-en">first</span>() <span class="pl-c"><span class="pl-c">//</span> "foo"</span></pre>
    </div>
    <p>上面代码中，<code>list</code>是一个数组，本身并没有<code>first</code>方法。通过<code>call</code>方法，可以把<code>Enumerable_first</code>里面的方法，绑定到<code>list</code>，从而<code>list</code>就具有<code>first</code>方法。这就叫做“代理”（delegation），<code>list</code>对象代理了<code>Enumerable_first</code>的<code>first</code>方法。
    </p>
    <h2><a href="#含义"  class="anchor" id="user-content-含义">

    </a>含义
    </h2>
    <p>Mixin 这个名字来自于冰淇淋，在基本口味的冰淇淋上面混入其他口味，这就叫做 Mix-in。</p>
    <p>它允许向一个类里面注入一些代码，使得一个类的功能能够“混入”另一个类。实质上是多重继承的一种解决方案，但是避免了多重继承的复杂性，而且有利于代码复用。</p>
    <p>Mixin 就是一个正常的类，不仅定义了接口，还定义了接口的实现。</p>
    <p>子类通过在<code>this</code>对象上面绑定方法，达到多重继承的目的。</p>
    <p>很多库提供了 Mixin 功能。下面以 Lodash 为例。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">vowels</span>(<span
            class="pl-smi">string</span>) {
  <span class="pl-k">return</span><span class="pl-sr"> <span class="pl-pds">/</span><span
                class="pl-c1">[aeiou]</span><span class="pl-pds">/</span>i</span>.<span class="pl-c1">test</span>(<span
                class="pl-c1">this</span>.<span class="pl-c1">value</span>);
}

<span class="pl-k">var</span> obj <span class="pl-k">=</span> { value<span class="pl-k">:</span> <span
                class="pl-s"><span class="pl-pds">'</span>hello<span class="pl-pds">'</span></span> };
<span class="pl-smi">_</span>.<span class="pl-en">mixin</span>(obj, {vowels<span class="pl-k">:</span> vowels})
<span class="pl-smi">obj</span>.<span class="pl-en">vowels</span>() <span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码通过 Lodash 库的<code>_.mixin</code>方法，让<code>obj</code>对象继承了<code>vowels</code>方法。</p>
    <p>Underscore 的类似方法是<code>_.extend</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> <span
            class="pl-en">Person</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span
            class="pl-smi">fName</span>, <span class="pl-smi">lName</span>) {
  <span class="pl-c1">this</span>.<span class="pl-smi">firstName</span> <span class="pl-k">=</span> fName;
  <span class="pl-c1">this</span>.<span class="pl-smi">lastName</span> <span class="pl-k">=</span> lName;
}

<span class="pl-k">var</span> sam <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Person</span>(<span class="pl-s"><span class="pl-pds">'</span>Sam<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>Lowry<span
                class="pl-pds">'</span></span>);

<span class="pl-k">var</span> NameMixin <span class="pl-k">=</span> {
  <span class="pl-en">fullName</span><span class="pl-k">:</span> <span class="pl-k">function</span> () {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>.<span class="pl-smi">firstName</span> <span
                class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span> <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-c1">this</span>.<span
                class="pl-smi">lastName</span>;
  },
  <span class="pl-en">rename</span><span class="pl-k">:</span> <span class="pl-k">function</span>(<span class="pl-smi">first</span>, <span
                class="pl-smi">last</span>) {
    <span class="pl-c1">this</span>.<span class="pl-smi">firstName</span> <span class="pl-k">=</span> first;
    <span class="pl-c1">this</span>.<span class="pl-smi">lastName</span> <span class="pl-k">=</span> last;
    <span class="pl-k">return</span> <span class="pl-c1">this</span>;
  }
};
<span class="pl-smi">_</span>.<span class="pl-en">extend</span>(<span class="pl-smi">Person</span>.<span class="pl-c1">prototype</span>, NameMixin);
<span class="pl-smi">sam</span>.<span class="pl-en">rename</span>(<span class="pl-s"><span class="pl-pds">'</span>Samwise<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>Gamgee<span
                class="pl-pds">'</span></span>);
<span class="pl-smi">sam</span>.<span class="pl-en">fullName</span>() <span class="pl-c"><span class="pl-c">//</span> "Samwise Gamgee"</span></pre>
    </div>
    <p>
        上面代码通过<code>_.extend</code>方法，在<code>sam</code>对象上面（准确说是它的原型对象<code>Person.prototype</code>上面），混入了<code>NameMixin</code>类。
    </p>
    <p><code>extend</code>方法的实现非常简单。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">extend</span>(<span
            class="pl-smi">destination</span>, <span class="pl-smi">source</span>) {
  <span class="pl-k">for</span> (<span class="pl-k">var</span> k <span class="pl-k">in</span> source) {
    <span class="pl-k">if</span> (<span class="pl-smi">source</span>.<span class="pl-en">hasOwnProperty</span>(k)) {
      destination[k] <span class="pl-k">=</span> source[k];
    }
  }
  <span class="pl-k">return</span> destination;
}</pre>
    </div>
    <p>上面代码将<code>source</code>对象的所有方法，添加到<code>destination</code>对象。</p>
    <h2><a href="#trait"  class="anchor" id="user-content-trait">

    </a>Trait
    </h2>
    <p>Trait 是另外一种多重继承的解决方案。它与 Mixin 很相似，但是有一些细微的差别。</p>
    <ul>
        <li>Mixin 可以包含状态（state），Trait 不包含，即 Trait
            里面的方法都是互不相干，可以线性包含的。比如，<code>Trait1</code>包含方法<code>A</code>和<code>B</code>，<code>Trait2</code>继承了<code>Trait1</code>，同时还包含一个自己的方法<code>C</code>，实际上就等同于直接包含方法<code>A</code>、<code>B</code>、<code>C</code>。
        </li>
        <li>对于同名方法的碰撞，Mixin 包含了解决规则，Trait 则是报错。</li>
    </ul>
</div>
</body>
</html>
